package com.qunar.tech.team12.shell.command;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.qunar.tech.team12.shell.command.annotation.Opt;
import com.qunar.tech.team12.shell.exception.NoSuchCommandException;
import com.qunar.tech.team12.shell.exception.NoSuchOption;
import com.qunar.tech.team12.shell.io.Input;
import com.qunar.tech.team12.shell.io.Output;
import com.qunar.tech.team12.shell.io.impl.Pipeline;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * Created by mzy on 14-7-23.
 */
public class Command {

    private final Map<String, Method> methods = Maps.newHashMap();
    private List<String> options = Lists.newArrayList();
    private List<String> params = Lists.newArrayList();
    private Output output = Output.CONSOLE;
    private Input input;

    private Object instance;

    public Command(Class clazz) throws NoSuchCommandException {
        try {
            this.instance = clazz.newInstance();
        } catch (Exception e) {
            throw new NoSuchCommandException("no such command" + clazz.getName());
        }
        setUpMethod();
    }

    private void setUpMethod() {
        Method[] methodArray = instance.getClass().getMethods();
        for (Method m : methodArray) {
            if (m.isAnnotationPresent(Opt.class)) {
                Opt opt = m.getAnnotation(Opt.class);
                methods.put(opt.shortName(), m);
            }
        }
    }

    public void execute() throws InvocationTargetException, IllegalAccessException, NoSuchOption {
        if (!params.isEmpty() && input instanceof Pipeline) {
            ((Pipeline) input).writeAll(params);
        }
        if (options.isEmpty()) {
            Method method = methods.get("default");
            if (method == null) {
                throw new NoSuchOption("No default method");
            }
            output.write(method.invoke(instance, input).toString());
        } else {
            for (String option : options) {
                Method method = methods.get(option);
                if (method == null) {
                    throw new NoSuchOption("No such option");
                }
                output.write(method.invoke(instance, input).toString());
            }
        }

    }

    public void setOptions(List<String> options) {
        this.options = options;
    }

    public List<String> getParams() {
        return params;
    }

    public void setParams(List<String> params) {
        this.params = params;
    }

    public void setOutput(Output output) {
        this.output = output;
    }

    public void setIput(Input input) {
        this.input = input;
    }

}
